from utils.crawl_tool_for_py3_v6 import crawlerTool
# from utils.qianfan_util import QianFan
import efinance as ef
from MyTT import *
from utils.e_mail import sendMail
from datetime import datetime, timedelta
from utils.log_util import saveLog
from utils.db_util import SqliteTool
from utils.time_util import fmt_d, stamp_time, YYYYMMDD
from constant.param_config import getConfigPath


class BaseStockPrice():

    # 请求n天内的交易数据
    limitAgoDay = 60
    # 最高价限制
    limitMaxPrice = 5
    # 价格限定之外的股票
    testCode = {}
    # kdj买卖分界线
    bottomLimit = 50
    # 最低涨跌幅
    limitMinUp = 0

    def __init__(self):
        super().__init__()
        self.request = crawlerTool()
        # self.qianfan = QianFan()

    def initThread(self):
        """
        初始化线程
        """

        tables = [
            "article_stock", "record_stock", "code_stock", "volume_stock"
        ]
        dbPath = getConfigPath() + "\stock.db"

        self.sqlite = SqliteTool(dbPath=dbPath)
        self.sqlite.create_table_list(tableName=tables)

    def updateStockPrice(self):
        """
        更新所有股票价格
        """

        # 以时间戳形式获取股票价格的开始和结束时间  getStockAgoDate入参：n天前
        agoStartTimestamp = self.getStockAgoDate(self.limitAgoDay)
        agoEndTimestamp = self.getStockAgoDate(1)

        self.deleteStockPrice(agoStartTimestamp)

        stockList = [
            item[0] for item in self.sqlite.query_many(
                "SELECT DISTINCT code FROM code_stock")
        ]

        self.update(stockList, agoStartTimestamp, agoEndTimestamp)

    def deleteStockPrice(self, agoStartTimestamp):
        """
        删除不在计划日期内的价格
        """

        # 删除小于开始时间的数据，保证数据在 limitAgoDay 限制天数内
        stockOldValue = self.sqlite.query_many(
            "SELECT * FROM volume_stock WHERE date < " +
            str(agoStartTimestamp))
        if stockOldValue is not None and len(stockOldValue) != 0:
            saveLog("删除计划外的价格信息...")
            self.sqlite.delete_record(
                "DELETE FROM volume_stock WHERE date < " +
                str(agoStartTimestamp))
            saveLog("计划外的价格信息删除完成")

    def requestHistory(self, stockList, agoStartTimestamp, agoEndTimestamp):
        """
        请求历史成交量
        """
        try:
            # 将开始和结束时间转为string类型
            startDate = fmt_d(agoStartTimestamp, "%Y%m%d")
            endDate = fmt_d(agoEndTimestamp, "%Y%m%d")

            maxId = self.getMaxId(tableName="volume_stock")
            historyInfo = ef.stock.get_quote_history(stockList,
                                                     beg=startDate,
                                                     end=endDate)

            insertValueList = []
            for item, value in historyInfo.items():
                for index in range(0, len(value)):
                    b = value.iloc[index]
                    c = str(int(b.成交量))
                    d = b.日期
                    e = str(b.开盘)
                    f = str(b.收盘)
                    g = str(b.最高)
                    h = str(b.最低)
                    i = str(b.换手率)
                    if c != "-" and d != "-":
                        nowTime = str(int(datetime.timestamp(datetime.now())))
                        volumeDate = str(stamp_time(d, fmt_str=YYYYMMDD))
                        maxId = maxId + 1
                        insertValueList.append(
                            tuple([
                                maxId, b.股票名称, b.股票代码, c, e, f, g, h, i,
                                volumeDate, nowTime
                            ]))

            chunkSize = int(len(insertValueList) / 50)
            splitResult = [
                insertValueList[i:i + chunkSize]
                for i in range(0, len(insertValueList), chunkSize)
            ]
            for item in splitResult:
                self.sqlite.operate_many(
                    'insert into volume_stock values (?,?,?,?,?,?,?,?,?,?,?)',
                    item)
        except Exception as e:
            saveLog("请求历史成交量异常:" + str(e))

    def update(self, stockList, agoStartTimestamp, agoEndTimestamp):
        """
        更新
        """

        allCode = [
            item[0] for item in self.sqlite.query_many(
                "SELECT DISTINCT code FROM volume_stock")
        ]

        if len(allCode) != 0:
            # 含有最新日期的股票
            newDayCode = [
                item[0] for item in self.sqlite.query_many(
                    "SELECT DISTINCT code FROM volume_stock WHERE date =" +
                    str(agoEndTimestamp))
            ]

            if len(newDayCode) != 0:
                if len(newDayCode) != allCode:
                    # 取出不含有新日期的股票
                    noPriceCodes = [
                        item for item in stockList if item not in newDayCode
                    ]
                    if len(noPriceCodes) != 0:
                        maxDate = self.sqlite.query_many(
                            "SELECT code, MAX(date) AS max_date FROM volume_stock GROUP BY code"
                        )
                        if maxDate != None and len(maxDate) != 0:
                            dictValue = {key: value for key, value in maxDate}
                            minDate = min({
                                key: value
                                for key, value in dictValue.items()
                                if key in noPriceCodes
                            }.values())
                            self.sqlite.delete_record(
                                "DELETE FROM volume_stock WHERE date > " +
                                str(minDate))

                            saveLog("价格数据统一处理完成，差异数据下载中...")
                            self.requestHistory(stockList, minDate,
                                                agoEndTimestamp)
                            saveLog("差异数据下载完成")
            else:
                # 没有最新日期，更新所有股票的最新日期
                saveLog("最新价格数据下载中...")
                self.requestHistory(stockList, agoEndTimestamp,
                                    agoEndTimestamp)
                saveLog("最新价格数据下载完成")
        else:
            # 没有价格数据，更新全部
            saveLog("价格数据为空，数据初始化中...")
            self.requestHistory(stockList, agoStartTimestamp, agoEndTimestamp)
            saveLog("数据初始化完成")

    def signalDetection(self, codeList):
        """
        信号检测
        """
        stockList, filterStock = self.getStandardSocket(codeList)
        volume, openPrice, closePrice, highPrice, lowPrice, stockName = self.getPriceList(
            filterStock, stockList)

        if len(volume) == 0 or len(openPrice) == 0 or len(
                closePrice) == 0 or len(highPrice) == 0 or len(lowPrice) == 0:
            return None

        volumeList = []
        kdjList = []
        mvList = []
        barList = []
        nameFilter = []
        averageDict = {}
        for item in stockList:
            volumeArr = volume[item][::-1]
            openArr = openPrice[item][::-1]
            closeArr = closePrice[item][::-1]
            highArr = highPrice[item][::-1]
            lowArr = lowPrice[item][::-1]
            name = stockName[item]

            # 根据成交量状态筛选股票
            if self.volumeCheck(closeArr, volumeArr):
                volumeList.append(item)

            # 根据kdj状态筛选股票
            if self.kdjCheck(closeArr, highArr, lowArr):
                kdjList.append(item)

            # 根据移动平均线状态筛选股票
            if self.mvCheck(closeArr):
                mvList.append(item)

            # 是否连续上涨
            if self.bullBar(openArr, closeArr, highArr):
                barList.append(item)

            # 成交量平均值
            averageDict[item] = self.volumeAverage(volumeArr)

            # 成交量大于15万，且大于昨日成交量或者今日开盘价大于昨日收盘价
            # 过滤ST
            if volumeArr[-1] > 150000 and (volumeArr[-1] > volumeArr[-2]
                                           or openArr[-1] > closeArr[-2]):
                if "ST" not in name:
                    if closeArr[-1] > openArr[-1]:
                        nameFilter.append(item)

        stocks = []
        for item in barList:
            if (not volumeList or item
                    in volumeList) and (not kdjList or item in kdjList) and (
                        not mvList or item in mvList) and (not nameFilter or
                                                           item in nameFilter):
                stocks.append(item)

        resultDict = {
            key: value
            for key, value in averageDict.items() if key in stocks
        }
        sortedResult = sorted(resultDict.items(),
                              key=lambda x: x[1],
                              reverse=True)[:5]
        return [item[0]
                for item in sortedResult] if len(sortedResult) != 0 else []

    def getStandardSocket(self, codeList):
        """
        获取符合要求的股票信息
        """

        filterStock = []
        stockCode = []

        try:
            result = ef.stock.get_realtime_quotes()
            for item in range(0, len(result)):
                itemInfo = result.iloc[item]

                if (self.testCode is not None and len(self.testCode) != 0
                        and itemInfo.股票代码 in self.testCode.keys()) or (
                            itemInfo.最新价 != "-" and itemInfo.最新价
                            < self.limitMaxPrice and itemInfo.涨跌幅 != "-"
                            and itemInfo.涨跌幅 > self.limitMinUp and
                            (itemInfo.股票代码 in codeList
                             or itemInfo.股票名称 in codeList)):
                    filterStock.append(itemInfo)
                    stockCode.append(itemInfo.股票代码)
        except Exception as e:
            print("获取股票实时价格失败：" + str(e))
            saveLog("获取股票实时价格失败")
        return stockCode, filterStock

    def getPriceList(self, filterStock, stockList):
        """
        获取股票价格信息
        """

        lastVolume = {item.股票代码: item.成交量 for item in filterStock}
        lastOpen = {item.股票代码: item.今开 for item in filterStock}
        lastClose = {item.股票代码: item.最新价 for item in filterStock}
        lastHigh = {item.股票代码: item.最高 for item in filterStock}
        lastLow = {item.股票代码: item.最低 for item in filterStock}
        stockName = {item.股票代码: item.股票名称 for item in filterStock}

        sortedData = self.checkDB(stockList, filterStock)
        volumeArr = {
            key: [item[3] for item in value]
            for key, value in sortedData.items()
        }
        openPrice = {
            key: [item[4] for item in value]
            for key, value in sortedData.items()
        }
        closePrice = {
            key: [item[5] for item in value]
            for key, value in sortedData.items()
        }
        highPrice = {
            key: [item[6] for item in value]
            for key, value in sortedData.items()
        }
        lowPrice = {
            key: [item[7] for item in value]
            for key, value in sortedData.items()
        }
        volume = self.instertLatest(volumeArr, lastVolume)
        openPrice = self.instertLatest(openPrice, lastOpen)
        closePrice = self.instertLatest(closePrice, lastClose)
        highPrice = self.instertLatest(highPrice, lastHigh)
        lowPrice = self.instertLatest(lowPrice, lastLow)
        return volume, openPrice, closePrice, highPrice, lowPrice, stockName

    def checkDB(self, stockList, filterStock):
        """
        数据检查
        """
        lastDate = {
            item.股票代码: stamp_time(item.最新交易日, fmt_str=YYYYMMDD)
            for item in filterStock
        }

        # self.volumeDB(stockList)
        volumeData = self.sqlite.query_many("SELECT * FROM volume_stock")
        groupValue = self.groupedData(volumeData)

        # 时间排序
        sortedData = {
            key: sorted(value, key=lambda x: x[9], reverse=True)
            for key, value in groupValue.items()
        }

        # 从本地数据库获取最后一个交易日之前的所有数据
        stockInfoList = {}
        for key, value in sortedData.items():
            if key in lastDate:
                dateValue = lastDate[key]
                stockInfoList[key] = [
                    item for item in value if item[9] < dateValue
                ]
        return stockInfoList

    def volumeCheck(self, close, volume):
        """
        成交量
        """
        vrResult = VR(close, volume)
        if vrResult[-1] < 200 and vrResult[-1] > 60 and vrResult[
                -1] > vrResult[-2]:
            return True
        return False

    def volumeAverage(self, volume):
        """
        5日内成交量平均值
        """
        return int((volume[-1] + volume[-2] + volume[-3]) / 3)

    def bullBar(self, open, close, high):
        """
        连续上涨
        """
        if close[-1] > open[-1] and close[-2] > open[-2] and close[-3] > open[
                -3]:
            return True
        elif close[-1] > high[-3]:
            return True
        return False

    def kdjCheck(self, close, high, low):
        """
        kdj粘合、j值上挑
        """
        try:
            kdjValue = KDJ(close, high, low)
            k = kdjValue[0][-1]
            d = kdjValue[1][-1]
            j = kdjValue[2][-1]
            lastJ = kdjValue[2][-3]
            if abs(k - d) < 10 and abs(k - j) < 10 and abs(d - j) < 10:
                if j > lastJ and lastJ < d and lastJ < k:
                    if k < self.bottomLimit and d < self.bottomLimit and j < self.bottomLimit and lastJ < self.bottomLimit:
                        return True
        except Exception as e:
            return False
        return False

    def mvCheck(self, close):
        """
        均线呈下跌趋势或者共振上涨
        """
        try:
            m5Vlaue = MA(close, 5)
            m10Vlaue = MA(close, 10)
            m20Vlaue = MA(close, 20)
            m30Vlaue = MA(close, 30)

            if m5Vlaue[-1] > m5Vlaue[-5] and m10Vlaue[-1] > m10Vlaue[
                    -5] and m20Vlaue[-1] > m20Vlaue[-5] and m30Vlaue[
                        -1] > m30Vlaue[-5] and m5Vlaue[-5] > m10Vlaue[
                            -5] and m10Vlaue[-5] > m20Vlaue[-5] and m20Vlaue[
                                -5] > m30Vlaue[-5]:
                return True
            elif m5Vlaue[-1] < m5Vlaue[-5] and m10Vlaue[-1] < m10Vlaue[
                    -5] and m5Vlaue[-1] < m10Vlaue[-1] and m5Vlaue[
                        -5] < m10Vlaue[-5]:
                return True
            elif m10Vlaue[-1] < m10Vlaue[-5] and m20Vlaue[-1] < m20Vlaue[
                    -5] and m10Vlaue[-1] < m20Vlaue[-1] and m10Vlaue[
                        -5] < m20Vlaue[-5]:
                return True
            elif m20Vlaue[-1] < m20Vlaue[-5] and m30Vlaue[-1] < m30Vlaue[
                    -5] and m20Vlaue[-1] < m30Vlaue[-1] and m20Vlaue[
                        -5] < m30Vlaue[-5]:
                return True
        except Exception as e:
            return False
        return False

    def getMaxId(self, tableName):
        """
        获取记录最大id
        """

        maxId = self.sqlite.query_one("SELECT MAX(id) FROM " + tableName)
        if len(maxId) != 0 and maxId[0] is not None:
            maxId = maxId[0] + 1
        else:
            maxId = 0
        return maxId

    def groupedData(self, data):
        """
        分组
        """
        grouped_data = {}
        for sublist in data:
            key = sublist[2]
            if key in grouped_data:
                grouped_data[key].append(sublist)
            else:
                grouped_data[key] = [sublist]
        return grouped_data

    def getLivePlate(self, stocks):
        """
        获取股票所属板块
        """

        plateName = {}
        for name in stocks:
            try:
                result = ef.stock.get_belong_board(name)
                plateName[name] = "、".join(result["板块名称"].values)
            except Exception as e:
                print("获取失败:" + str(e))
                saveLog("获取失败:" + str(e))
        return plateName

    def instertLatest(self, oldList, latestList):
        """
        插入最新值
        """

        for key in oldList:
            if key in latestList:
                value_b = latestList[key]
                oldList[key].insert(0, value_b)
        return oldList

    def getStockAgoDate(self, ago):
        """
        从数据库获取指定日期，周六日除外
        """

        currentDay = datetime.today()
        todayZeroHour = currentDay.replace(hour=0,
                                           minute=0,
                                           second=0,
                                           microsecond=0)

        # 逐一向前推算，直到找到满足条件的日期
        count = 0
        while count < ago:
            todayZeroHour -= timedelta(days=1)
            # 如果是周六或周日，继续向前推一天
            if todayZeroHour.weekday() in [5, 6]:
                continue
            count += 1

        return int(todayZeroHour.timestamp())
